#pragma once

#include "log.hpp"
#include "mutex.hpp"
#include "Cond.hpp"
#include <unistd.h>
#include "Mythread.hpp"
#include <queue>
#include <vector>
#include <functional>

namespace ThreadPoolModule
{
    using thread_t = std::shared_ptr<ThreadModule::Mythread>;
    using namespace LogModule;
    using namespace MutexModule;
    using namespace CondModule;
    using namespace ThreadModule;
    static int default_num = 5;
    template <typename T>
    class ThreadPool
    {
    private:
        bool IsEmpty() // 判断任务队列是否为空
        {
            return _taskqueue.empty();
        }

        void HandlerTask(std::string name) // 回调函数，负责在线程初始化时调用
        {
            LOG(LogLevel::INFO) << "线程" << name << "开始执行回调函数逻辑";
            while (true)
            {
                T t;
                {
                    LockGuard lock(_mutex);
                    while (IsEmpty() && is_running)
                    {
                        wait_num++;
                        _cond.Wait(_mutex);
                        wait_num--;
                    }

                    if (IsEmpty() && !is_running)
                    {
                        break; // 退出回调函数
                    }

                    // 此时只会出现有任务的情况，无任务且is_running的情况已经在while循环中处理了，无任务且！is_running的情况再上面if中处理了
                    t = _taskqueue.front(); // 拿出任务
                    _taskqueue.pop();       // 删除任务
                }

                // 执行任务不用上锁
                t(); // 执行任务，我们这里规定传进来的任务必须重载()运算符
            }

            LOG(LogLevel::INFO) << "线程" << name << "执行回调函数逻辑结束";
        }

        ThreadPool(const ThreadPool &) = delete;               // 禁止拷贝函数
        ThreadPool<T> &operator=(const ThreadPool &) = delete; // 禁止赋值函数
        ThreadPool(int num = default_num)                      // 将构造函数放在私人权限中防止外部随意创建对象
            : _num(num), is_running(false), wait_num(0)
        {
            for (int i = 0; i < _num; ++i)
            {
                _threadpool.push_back(std::make_shared<Mythread>(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1)));
                // 我们这里使用bind的原因是因为HandlerTask是一个类成员函数,只传递函数名会导致函数签名不匹配，
                // 我们想要在不是这个类的线程变量中调用这个函数，需要需要 this 提供调用上下文
                // 我们需要将this指针绑定到HandlerTask函数中，这样才能在HandlerTask函数中访问到线程池的成员变量
            }
        }

    public:
        static ThreadPool<T> *getInstance()
        {
            if (instance == NULL)
            {
                LockGuard lockguard(mutex);
                if (instance == NULL)
                {
                    LOG(LogLevel::INFO) << "单例首次被执行，需要加载对象...";
                    instance = new ThreadPool<T>();
                    instance->Start(); // 启动线程池
                }
            }

            return instance;
        }
        void Stop()
        {
            if (is_running)
            {
                is_running = false;
                // 此时不能再入任务队列了
                if (wait_num > 0)
                {
                    _cond.SignalAll(); // 唤醒所有线程
                }
            }
        }

        void Start()
        {
            is_running = true;
            for (auto &it : _threadpool)
            {
                LOG(LogLevel::INFO) << "启动线程" << it->Name() << "...成功";
                it->start();
            }
        }

        void Equeue(T &&task) // 我们这里的线程池是一个模板，这个模板的划分是根据我们传进来的任务类型来划分的。
        // 所以我们这里要使用模板参数T
        // 这里的T && task是一个语法：引用折叠
        // 如果我们传进来的是一个左值，那么T && task会被折叠成T & &&，根据引用折叠规则，会被折叠成T &
        // 如果我们传进来的是一个右值，那么T && task会被折叠成T && &&，根据引用折叠规则，会被折叠成T &&
        // 如果我们传进来的是一个task_t，T&& 就是普通的右值引用（task_t&&）
        {
            LockGuard lock(_mutex);
            if (!is_running)
            {
                return; // 如果不为运行状态，就不能新入任务
            }
            _taskqueue.push(std::forward<T>(task)); // 我们这里使用std::forward来进行完美转发
            LOG(LogLevel::INFO) << "任务入队列成功";

            if (wait_num > 0)
            {
                _cond.SignalAll(); // 唤醒所有线程
            }
        }
        void Wait()
        {
            for (auto &it : _threadpool)
            {
                it->join();
                LOG(LogLevel::INFO) << "回收线程" << it->Name() << "...成功";
            }
        }

    private:
        std::vector<thread_t> _threadpool; // 线程管理数组
        std::queue<T> _taskqueue;          // 任务队列
        int _num;                          // 线程数量
        Mutex _mutex;                      // 锁
        Cond _cond;                        // 条件变量
        int wait_num;                      // 进行等待的线程数量
        bool is_running;                   // 线程池是否在运行

        static ThreadPool<T> *instance;
        static Mutex mutex; // 只用来保护单例
    };
    template <typename T> // static的类成员变量的初始化需要放在类外
    ThreadPool<T> *ThreadPool<T>::instance = NULL;
    template <typename T>
    Mutex ThreadPool<T>::mutex; // 只用来保护单例
}
